﻿using System;
using System.Collections.Concurrent;
using System.Data.Common;
using System.Data.Entity.Infrastructure.Interception;
using System.Text;
using System.Text.RegularExpressions;
using Yz.Base;
using Yz.Core.Tools;

namespace Yz.Core.DBUtility
{
    /// <summary>
    ///     EF数据操作追踪日志
    /// </summary>
    public class EFDbLogger : IDbCommandInterceptor
    {
        private static readonly ConcurrentDictionary<DbCommand, DateTime> MStartTime =
            new ConcurrentDictionary<DbCommand, DateTime>();

        public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            Log(command, interceptionContext); 
        }

        public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            var sql = command.CommandText;
            if (command.CommandText.IsUpdateDB(ref sql))
            {
                command.CommandText = sql;
            }
            if (command.CommandText.CheckEFMigration())
            {
                command.CommandTimeout = 1800;
            } 
            OnStart(command);
        }

        public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            Log(command, interceptionContext); 
        }

        public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            OnStart(command);
        }

        public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            Log(command, interceptionContext);
        }

        public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            OnStart(command);
        }

        /// <summary>
        ///     记录开始执行时的时间
        /// </summary>
        /// <param name="command"></param>
        private static void OnStart(DbCommand command)
        {
            MStartTime.TryAdd(command, DateTime.Now);
        }

        /// <summary>
        ///     记录EF生成语句与执行时间
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="command"></param>
        /// <param name="interceptionContext"></param>
        private static void Log<T>(DbCommand command, DbCommandInterceptionContext<T> interceptionContext)
        {
            TimeSpan duration;
            var dbName = command.Connection.Database;
            var comSql = command.CommandText;
            var isMigration = comSql.CheckEFMigration();
            //得到此command的开始时间
            MStartTime.TryRemove(command, out DateTime startTime);
            if (startTime != default)
                duration = DateTime.Now - startTime;
            else
            {
                duration = TimeSpan.Zero;
            }

            var sqlsb = new StringBuilder();
            sqlsb.AppendLine("服务器：" + command.Connection.DataSource + "；");
            sqlsb.AppendLine("数据库：" + dbName + "；");
            sqlsb.AppendLine("SQL语句：" + comSql + "；");
            sqlsb.AppendLine("相关参数：");
            //循环获取执行语句的参数值
            foreach (DbParameter param in command.Parameters)
                sqlsb.AppendLine(param.DbType + " " + param.ParameterName + " = " + param.Value + "；");
            sqlsb.AppendLine("执行时间：" + duration.TotalSeconds + " 秒；");
            var tbName = GetTableNameBySql(comSql);
            if (interceptionContext.Exception != null)
            {
                if (interceptionContext.Exception.Message != "列名 'CreatedOn' 无效。")
                {
                    //EF数据操作出错后添加错误日志 
                    YzNLog.Error(interceptionContext.Exception, string.Format("EF生成语句执行出错：{0}，表：{1}，语句：{2}", dbName, tbName, sqlsb.ToString()));
                }
            }
            else if (duration.TotalSeconds >= BaseConfig.EFMaxTime)
            {
                //EF数据操作执行时间超长警告
                YzNLog.Warn(string.Format("EF数据跟踪 语句执行过长,库：{0}，表：{1}，语句：{2}", dbName, tbName, sqlsb.ToString()));
            }
            else if (BaseConfig.EnableEFLog)
            {
                if (isMigration)
                {
                    //EF数据库自动迁移语句记录
                    YzNLog.Trace(string.Format("EF数据跟踪 数据迁移语句,库：{0}，表：{1}，语句：{2}", dbName, tbName, sqlsb.ToString()));
                }
                else
                {
                    YzNLog.Trace(string.Format("EF数据跟踪 语句执行时间,库：{0}，表：{1}，语句：{2}", dbName, tbName, sqlsb.ToString()));
                }
            }
        }

        /// <summary>
        ///     从语句中匹配表名
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        private static string GetTableNameBySql(string sql)
        {
            var tbName = "";
            var reg = new Regex(@"(?<=\[dbo]\.\[)[\s\S]*?(?=\])");
            if (reg.IsMatch(sql))
            {
                var match = reg.Match(sql);
                var value = match.Groups[0].Value;
                tbName = value.TrimStart('[').TrimEnd(']');
            }

            return tbName;
        }

    }
}
